import { BaseModel } from '../base';
import { MODEL_NAME } from '../../constants/model-names';
import type { BaseSnapshot } from '../../systems/save';
import type { CultivationSkillItemId } from '../../data/item/cultivation-skill';
import { EVENTS, type IEventBus } from '../../events';
import { CultivationSkillManager } from './cultivation-skill-manager';
import type {
  CultivationSkill,
  CultivationSkillUpgradeProgressInfo,
} from './type';

interface CultivationSkillV1 extends BaseSnapshot {
  version: 1;
  learnedCultivationSkillIds: CultivationSkillItemId[];
  mainCultivationSkillId: CultivationSkillItemId | null;
}

interface CultivationSkillV2 extends Omit<CultivationSkillV1, 'version'> {
  version: 2;
  cultivationSkillLevels: Record<CultivationSkillItemId, number>;
}

interface CultivationSkillV3 {
  version: 3;
  learnedCultivationSkills: CultivationSkill[];
}

export type CultivationSkillSnapshot = CultivationSkillV3;

export type CultivationSkillSnapshotOld =
  | CultivationSkillV1
  | CultivationSkillV2
  | CultivationSkillV3;

export class CultivationSkillModel extends BaseModel {
  public name = MODEL_NAME.CULTIVATION_SKILL;
  private bus: IEventBus;
  private cultivationSkillManager = new CultivationSkillManager();
  private cultivationSkillUpgradeProgressInfo: CultivationSkillUpgradeProgressInfo[] =
    [];

  constructor(bus: IEventBus) {
    super();
    this.bus = bus;
  }

  /** 学习功法 */
  public learnSkill(id: string) {
    const ok = this.cultivationSkillManager.learnSkill(id);
    if (!ok) return;
    if (this.cultivationSkillManager.hasMainCultivationSkill() === false) {
      this.setMainCultivationSkill(id);
    }
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  public removeLearnedCultivationSkill(id: string) {
    this.cultivationSkillManager.removeLearnedCultivationSkill(id);
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  public setMainCultivationSkill(id: string) {
    const ok = this.cultivationSkillManager.setMainCultivationSkill(id);
    if (!ok) return;
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  public isLearned(id: string) {
    return this.cultivationSkillManager.isLearned(id);
  }

  public getPlayerSkillsInfo() {
    return this.cultivationSkillManager.getPlayerSkillsInfo();
  }

  public getPlayerSkill(id: string) {
    return this.cultivationSkillManager.getPlayerSkill(id);
  }

  public getItemConfig(id: string) {
    return this.cultivationSkillManager.getItemConfig(id);
  }

  public upgradeSkill(id: string) {
    this.cultivationSkillManager.upgradeSkill(id);
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  public resetCultivationSkillProgress(id: string) {
    const ok = this.cultivationSkillManager.resetCultivationSkillProgress(id);
    if (!ok) return;
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  /** 更新击杀敌人 */
  public updateCultivationKillEnemies(enemyIds: string[]) {
    this.cultivationSkillManager.updateCultivationKillEnemies(enemyIds);
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  /** 获取学习的功法数量 */
  public getLearnedCultivationSkillCount() {
    return this.cultivationSkillManager.getLearnedCultivationSkillCount();
  }

  public setCultivationSkillUpgradeProgressInfo(
    cultivationSkillUpgradeProgressInfo: CultivationSkillUpgradeProgressInfo[]
  ) {
    this.cultivationSkillUpgradeProgressInfo =
      cultivationSkillUpgradeProgressInfo;
    this.bus.emit(EVENTS.CULTIVATION_SKILL_STATE_UPDATE);
  }

  public getCultivationSkillUpgradeProgressInfo() {
    return this.cultivationSkillUpgradeProgressInfo;
  }

  public isCultivationSkillLearned(id: string) {
    return this.cultivationSkillManager.isCultivationSkillLearned(id);
  }

  public get uiState() {
    return {
      learnedCultivationSkills: this.getPlayerSkillsInfo(),
      cultivationSkillUpgradeProgressInfo:
        this.getCultivationSkillUpgradeProgressInfo(),
    };
  }

  public save(): CultivationSkillSnapshot {
    const learnedCultivationSkills = this.getPlayerSkillsInfo().map(
      ({ skill }) => skill
    );
    return {
      version: 3,
      learnedCultivationSkills,
    };
  }

  public load(snapshot: CultivationSkillSnapshot) {
    this.cultivationSkillManager.setPlayerSkills(
      snapshot.learnedCultivationSkills
    );
  }

  /** 迁移 */
  public migrate(old: CultivationSkillSnapshotOld): CultivationSkillSnapshot {
    let current = old;
    const TARGET = 3;

    while (current.version !== TARGET) {
      current = this.upgradeOnce(current);
    }

    return current as CultivationSkillSnapshot;
  }
  private upgradeOnce(
    snapshot: CultivationSkillSnapshotOld
  ): CultivationSkillSnapshotOld {
    const currentVer = snapshot.version ?? 1;

    switch (currentVer) {
      case 1:
        return this.migrateV1toV2(snapshot as CultivationSkillV1);
      case 2:
        return this.migrateV2toV3(snapshot as CultivationSkillV2);
      default:
        throw new Error(`unknown snapshot version ${currentVer}`);
    }
  }
  private migrateV1toV2(snapshot: CultivationSkillV1): CultivationSkillV2 {
    const learnedCultivationSkillIds =
      snapshot.learnedCultivationSkillIds || [];
    const cultivationSkillLevels: Record<CultivationSkillItemId, number> =
      {} as Record<CultivationSkillItemId, number>;
    for (const id of learnedCultivationSkillIds) {
      cultivationSkillLevels[id] = 1;
    }
    return {
      version: 2,
      learnedCultivationSkillIds: snapshot.learnedCultivationSkillIds,
      mainCultivationSkillId: snapshot.mainCultivationSkillId,
      cultivationSkillLevels,
    };
  }
  private migrateV2toV3(snapshot: CultivationSkillV2): CultivationSkillV3 {
    const learnedKills = snapshot.learnedCultivationSkillIds
      .map((id) => {
        return this.cultivationSkillManager.getItemConfig(id);
      })
      .filter((v) => v !== undefined);

    const learnedCultivationSkills = learnedKills.map((v) => {
      return this.cultivationSkillManager.createPlayerCultivationSkill(v);
    });

    const mainSkill = learnedCultivationSkills.find((v) => {
      return v.skillId === snapshot.mainCultivationSkillId;
    });
    if (mainSkill) {
      mainSkill.isMain = true;
    }

    return {
      version: 3,
      learnedCultivationSkills,
    };
  }
}
